package com.boil.qz.safekavass.service;

import com.boil.qz.safekavass.model.ResourceEntity;
import com.boil.util.Constants;
import com.boil.util.FastJsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.NamedFilterList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * Created by ChenYong on 2017-08-21.
 * <br>
 * Shiro 过滤器链管理器。
 * <br>
 * <p style="color: red;">
 * 备注：来自开涛的博客之《跟我学Shiro》：<a href="http://jinnianshilongnian.iteye.com/blog/2040929">第十九章 动态URL权限控制</a>
 * </p>
 */
@Service
public class ShiroFilerChainManager {
    /**
     * 日志
     */
    private static Logger log = LoggerFactory.getLogger(ShiroFilerChainManager.class);
    /**
     * 默认 Filter Chain
     */
    private Map<String, NamedFilterList> defaultFilterChains;
    /**
     * 注入资源 Service
     */
    @Autowired
    private ResourceService resourceService;
    /**
     * 注入默认过滤器链管理器
     */
    @Autowired
    private DefaultFilterChainManager filterChainManager;

    /**
     * 被“@PostConstruct”修饰的方法会在服务器加载 Servle 的时候运行，并且只会被服务器执行一次。
     * PostConstruct 在构造函数之后执行，在 init() 方法之前执行。
     */
    @PostConstruct
    public void init() {
        defaultFilterChains = new LinkedHashMap<String, NamedFilterList>(filterChainManager.getFilterChains());

        initFilterChains();
    }

    /**
     * 初始化过滤器链。
     * <br>
     * <p style="color: red;">备注：主要用于加载权限。</p>
     */
    public synchronized void initFilterChains() {
        // 1、删除旧的 Filter Chain；
        filterChainManager.getFilterChains().clear();

        // 2、注册默认的 Filter Chain；
        if (defaultFilterChains != null) {
            filterChainManager.getFilterChains().putAll(defaultFilterChains);
        }

        // 清空用户菜单 URL 缓存
        Constants.USER_MENU_URL_CACHE.clear();

        // 查询资源
        List<ResourceEntity> resourceEntities = resourceService.findByDeletedCode(Constants.VALUE_0);
        // 遍历资源
        Iterator<ResourceEntity> resourceEntityIterator = resourceEntities.iterator();
        while (resourceEntityIterator.hasNext()) {
            // 资源
            ResourceEntity resourceEntity = resourceEntityIterator.next();
            // 资源 URL
            String resourceUrl = resourceEntity.getResourceUrl();

            // 3、注册新的 Filter Chain；
            if (StringUtils.isNotBlank(resourceUrl)) {
                filterChainManager.addToChain(resourceUrl, Constants.PERMS_STR, resourceEntity.getPermissionCode());
            }

            /* 4、递归查找。
             * 如果资源是菜单，则将 URL 作为 Key，将 ID 和父 ID 构成的 Map 作为 Value；
             * 如果资源不是菜单，则将 URL 作为 Key，将父菜单 ID 和父菜单父 ID 构成的 Map 作为 Value。
             */
            recursionFindMenuUrlInCache(resourceEntity, resourceUrl);
        }

        log.info("Shiro过滤器链管理器--->缓存菜单 URL 数量：{}，加载权限数量：{}，权限列表：{}", Constants.USER_MENU_URL_CACHE.size(), filterChainManager.getChainNames().size(), FastJsonUtils.bean2Json(filterChainManager.getChainNames()));
    }

    /**
     * 递归找到菜单 URL 存入缓存中。
     *
     * @param resourceEntity 资源实例
     * @param resourceUrl    资源 URL
     */
    private void recursionFindMenuUrlInCache(ResourceEntity resourceEntity, String resourceUrl) {
        // 如果不是根资源
        if (resourceEntity.getResourceType() != Constants.VALUE_0) {
            // 如果是菜单
            if (resourceEntity.getResourceType() == Constants.VALUE_1) {
                if (StringUtils.isNotBlank(resourceUrl)) {
                    Map<String, Long> idAndParentIds = new HashMap<String, Long>();
                    idAndParentIds.put(Constants.MENU_ID, resourceEntity.getId());
                    idAndParentIds.put(Constants.MENU_PARENT_ID, resourceEntity.getParent().getId());

                    Constants.USER_MENU_URL_CACHE.put(resourceUrl, idAndParentIds);
                }

                // 否则，不是菜单，那么，递归找到父菜单
            } else {
                recursionFindMenuUrlInCache(resourceEntity.getParent(), resourceUrl);
            }
        }
    }
}